﻿using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Zocono.Infrastructure.Common;
using Zocono.WCS.Domain.BytesMapObjectDomain;
using Zocono.WCS.Domain.DomainRepository;
using Zocono.WCS.Domain.EntityInfo;
using Zocono.WCS.Domain.GlobalClass;
using Zocono.WCS.Domain.PlcConnectPoolDomain;
using Zocono.WCS.Domain.ProcessModuleDomain;

namespace Zocono.WCS.Domain.JobExcutorDomain
{
    public class JobExcutorService : IJobExcutorService
    {
        private readonly IJobInfoRepository _jobInfoRepository;
        private readonly IPlcReadDbInfoRepository _plcReadDbInfoRepository;
        private readonly IReadDbJobService _readDbJobService;
        private readonly IProcessModuleServiceFactory _processModuleServiceFactory;
        private readonly ILogUtil _logUtil;
        private readonly IPlcConnectionPoolService _plcConnectionPoolService;
        private readonly IRedisHelper _IRedisHelper;
        private readonly NlogUtilScoped _NlogUtilScoped;

        private string _programeCode, _jobName;


        public JobExcutorService(IRedisHelper iRedisHelper, IPlcConnectionPoolService plcConnectionPoolService, IJobInfoRepository jobInfoRepository, IPlcReadDbInfoRepository plcReadDbInfoRepository, IReadDbJobService readDbJobService, IProcessModuleServiceFactory processModuleServiceFactory, ILogUtil logUtil, NlogUtilScoped nlogUtilScoped)
        {
            _plcConnectionPoolService = plcConnectionPoolService;
            _jobInfoRepository = jobInfoRepository;
            _plcReadDbInfoRepository = plcReadDbInfoRepository;
            _readDbJobService = readDbJobService;
            _processModuleServiceFactory = processModuleServiceFactory;
            _logUtil = logUtil;
            _IRedisHelper = iRedisHelper;
            _NlogUtilScoped = nlogUtilScoped;
        }

        private static object _lock = new object();
        public async Task ReadPlcDbJobAsync(string programeCode, string jobName)
        {
            //while (true)
            //{
            //任务已锁定PLC，未释放
            if (_plcConnectionPoolService.S7CheckJobLockedConnect(jobName))
            {
                // _logUtil.Info($"Programe：{programeCode},Job:{jobName}，已锁定PLC未释放，本次Job退出");
                return;
            }
            try
            {
            //    _logUtil.Debug($"ReadPlcDbJobAsync:{programeCode},{jobName}");
                //锁定Job
                if (!_plcConnectionPoolService.S7LockJobReadPlcConnection(jobName))
                {
                    //_logUtil.Info($"Programe：{programeCode},Job:{jobName}，锁定PLC失败");
                    return;
                }
                //_logUtil.Info($"Programe：{programeCode},Job:{jobName}，成功锁定PLC，进入业务处理");
                _programeCode = programeCode;
                _jobName = jobName;
                var jobPlcReadDb = await GetJobPlcReadDb(_programeCode, _jobName);//数据库获取同一个作业的PLCReadDB信息，一般一个作业对应几个PLC
                if (jobPlcReadDb == null) { return; }

                //1、读PLC并返回处理模型列表,平面PLC在线离线状态处理模型，统一用ProcessModuleType=ProcessModuleName="PlcOnlineState"存储
                //2、可以考虑将释放PLC连接拿到外面来执行，控制释放的时间，以管控线程
                var processModules = await _readDbJobService.ReadPlcDbAsync(jobName, jobPlcReadDb, programeCode);//锁定plc

                //PLC在线离线状态
                //var plcStateProcessModule = processModules.Find(p => p.ProcessModuleType == "PlcOnlineState");

                foreach (var processModule in processModules)
                {
                    try
                    {
                        //if (processModule.ProcessModuleType == "PlcOnlineState")
                        //{
                        //    continue;
                        //}
                        //平面报文和堆垛机报文需要进入交互逻辑，其他报文不需要
                        if (processModule.ProcessModuleType != "ProcessModulePlanarService" && processModule.ProcessModuleType != "ProcessModuleScService" && processModule.ProcessModuleType != "ProcessModulePlanarFxService")
                        {
                            continue;
                        }
                        //区分平面报文和SC
                        var processModuleService = _processModuleServiceFactory.Create(processModule.ProcessModuleType);
                        await processModuleService.ProcessDbObjectAsync(processModule/*, plcStateProcessModule*/);
                    }
                    catch (Exception ex)
                    {
                        _logUtil.Error($"解析报文归档【{processModule.ProcessModuleType}】-【{processModule.ProcessModuleName}】-【{processModule.equipmentDbObjects?[0]?.EquipmentCode}】报错ex={ex.Message},堆栈stack={ex.StackTrace}");
                    }
                }
            }
            catch (Exception ex)
            {
                //_logUtil.Error(ex.ToString());
            }
            finally
            {

                //释放Job
                bool ret = _plcConnectionPoolService.S7FreeJobReadPlcConnection(jobName, programeCode);
                //_logUtil.Info($"Programe：{programeCode},Job:{jobName}，业务处理完成，释放锁定{(ret ? "成功" : "失败")}");
            }
            //}
        }

        /// <summary>
        /// 读取对应作业的PLC地址块
        /// </summary>
        /// <returns></returns>

        public async Task<PlcReadDbJobData> GetJobPlcReadDb(string _programeCode, string _jobName)
        {
            PlcReadDbJobData jobPlcReadDb = null;
            JobInfo jobInfo = new();
            List<PlcReadDbInfo> plcDbs = new();
            List<PlcReadDbInfo> plcreadDbs = new();
            try
            {
                var jobinfos = _IRedisHelper.GetStringKey<List<JobInfo>>("JobInfo");
                if (jobinfos != null)
                    jobInfo = jobinfos.Where(l => l.ProgrameCode == _programeCode && l.JobName == _jobName).Select(l => l).FirstOrDefault();
                if (jobInfo == null || jobinfos == null || jobinfos.Count == 0)
                {
                    jobinfos = await _jobInfoRepository.GetListAsync();
                    jobInfo = jobinfos.Where(l => l.ProgrameCode == _programeCode && l.JobName == _jobName).Select(l => l).FirstOrDefault();
                    //await _IRedisHelper.DeleteAsync("JobInfo");
                    var saveOk = _IRedisHelper.SetStringKey("JobInfo", jobinfos);
                    //_logUtil.Info("Redis没有JobInfo");
                }
                //var jobInfo = await _jobInfoRepository.GetFirstAsync(j => j.ProgrameCode == _programeCode && j.JobName == _jobName);

                if (jobInfo == null)
                {
                    //_logUtil.Error($"查询Job失败，Job不存在， programeCode：{_programeCode}，jobName：{_jobName} 失败");
                    return null;
                }

                plcreadDbs = _IRedisHelper.GetStringKey<List<PlcReadDbInfo>>("PlcReadDbInfo");
                if (plcreadDbs != null)
                    plcDbs = plcreadDbs.Where(l => l.JobID == jobInfo.ID && !l.IsHeartbeat && l.IsEnable).ToList();
                if (plcDbs == null || plcDbs.Count == 0 || plcreadDbs == null || plcreadDbs.Count == 0)
                {
                    plcreadDbs = await _plcReadDbInfoRepository.GetListAsync(c=>c.IsEnable);
                    plcDbs = plcreadDbs.Where(l => l.JobID == jobInfo.ID && !l.IsHeartbeat && l.IsEnable).ToList();
                    //await _IRedisHelper.DeleteAsync("PlcReadDbInfo");
                    var saveOk = _IRedisHelper.SetStringKey("PlcReadDbInfo", plcreadDbs);
                    //_logUtil.Info("Redis没有PlcReadDbInfo");
                }


                //plcDbs = await _plcReadDbInfoRepository.GetListAsync(pr => pr.JobID == jobInfo.ID && !pr.IsHeartbeat && pr.IsEnable);

                if (plcDbs.Count <= 0)
                {
                    //_logUtil.Error($"查询PlcReadDbInfo失败，没有对应的PlcReadDbInfo， programeCode：{_programeCode}，jobName：{_jobName} 失败");
                    return null;
                }

                jobPlcReadDb = new PlcReadDbJobData { ProgrameCode = _programeCode, JobName = _jobName, JobPlcDbs = plcDbs };

                return jobPlcReadDb;

            }
            catch (Exception ex)
            {
                _logUtil.Error($"获取读DB报错:{ex.ToString()}");
            }
            return jobPlcReadDb;
        }

        /// <summary>
        /// 读取PLC监控数据并保存到内存中
        /// </summary>
        /// <param name="proName"></param>
        /// <param name="jobsStr"></param>
        /// <returns></returns>
        public async Task ReadPlcDbMonitorAsync(string proName, List<string> jobsStr)
        {
            foreach (var item in jobsStr)
            {
                string key = $"{proName}_{item}";
                try
                {
                    _NlogUtilScoped.Debug($"读取PLCReadDB信息...{proName}_{item}");
                    //读取PLC监控数据
                    var jobPlcReadDb = await GetJobPlcReadDb(proName, item);//数据库获取同一个作业的PLCReadDB信息，一般一个作业对应几个PLC
                    _NlogUtilScoped.Debug($"读取报文解析...{proName}_{item}");
                    var processModules = await _readDbJobService.ReadPlanerPlcDbAsync(item, jobPlcReadDb, proName);
                    _NlogUtilScoped.Debug($"保存报文数据...{proName}_{item}");
                    //保存或这更新到内存
                    if (GlobalResource.ProcessModuleDicts.TryGetValue($"{proName}_{item}", out var proDict))
                    {
                        GlobalResource.ProcessModuleDicts.TryUpdate($"{key}", processModules, proDict);
                        _NlogUtilScoped.Debug($"获取PLC设备监控数据，更新成功:{key}");
                    }
                    else //不存在楼层数据 则加入
                    {
                        GlobalResource.ProcessModuleDicts.TryAdd($"{key}", processModules);
                        _NlogUtilScoped.Debug($"获取PLC设备监控数据，添加成功:{key}");
                    }
                    _NlogUtilScoped.Debug($"保存数据结束...{proName}_{item}");
                    await Task.Delay(2);
                }
                catch (Exception ex)
                {
                    _NlogUtilScoped.Error($"读取监控异常...{ex.Message}");
                }
                finally
                {
                    await Task.Delay(2);
                }
            }
        }


    }
}
